using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using JBORPG.Net.Attributes;
using System.Net;
using System.Xml;
using System.Net.Sockets;

namespace JBORPG.Net
{
    public class NetHandler
    {
        private List<NHType> m_Types;

        public NetHandler()
        {
            m_Types = new List<NHType>();
        }

        private Byte[] HandleShortInvoker(Invoker i)
        {
            try
            {
                //
                // search for the short name function
                MethodInfo method = null;
                NHType type = null;
                foreach (NHType t in m_Types)
                {
                    foreach (MethodInfo mi in t.Type.GetMethods())
                    {
                        NetMethod[] attributes = (NetMethod[])mi.GetCustomAttributes(typeof(NetMethod), false);
                        if (attributes.Length > 0)
                        {
                            if (attributes[0].ShortName == i.Method)
                            {
                                method = mi;
                                type = t;
                                break;
                            }
                        }
                    }

                    if (method != null)
                    {
                        break;
                    }
                }

                if (type == null || method == null)
                {
                    throw new Exception("Command not understood");
                }

                //
                // invoke method
                i.Return = method.Invoke(type.Instance, i.Parameters);

                return Helper.StringToBytes(Serializer.SerializeObject(i));
            }
            catch (Exception ex)
            {
                if (ex.InnerException != null)
                    ex = ex.InnerException;

                i.Return = ex;
                return Helper.StringToBytes(Serializer.SerializeObject(i));
            }
        }

        private Byte[] HandleInvoker(Invoker i)
        {
            try
            {
                //
                // get type
                NHType t = GetType(i.Type);
                if (t == null)
                {
                    throw new Exception("Command not understood");
                }

                //
                // get method
                MethodInfo mi = t.Type.GetMethod(i.Method);
                if (mi == null)
                {
                    throw new Exception("Command not understood");
                }

                //
                // check if valid method
                if (mi.GetCustomAttributes(typeof(NetMethod), false).Length == 0)
                {
                    throw new Exception("Command not understood");
                }

                //
                // invoke method
                i.Return = mi.Invoke(t.Instance, i.Parameters);

                return Helper.StringToBytes(Serializer.SerializeObject(i));
            }
            catch (Exception ex)
            {
                if (ex.InnerException != null)
                    ex = ex.InnerException;

                i.Return = ex;
                return Helper.StringToBytes(Serializer.SerializeObject(i));
            }
        }

        /// <summary>
        /// Handles the data and invokes the method.
        /// </summary>
        /// <param name="data">An array of bytes</param>
        /// <returns>Bytes with the return</returns>
        public Byte[] HandleData(Byte[] data)
        {
            try
            {
                Invoker i = (Invoker)Serializer.DeSerializeObject(Helper.BytesToString(data));

                if (i.Type == null || i.Type == "")
                {
                    return HandleShortInvoker(i);
                }
                else
                {
                    return HandleInvoker(i);
                }
            }
            catch (Exception ex)
            {
                return Helper.StringToBytes("BAD_DATA " + ex.Message);
            }
        }

        public Invoker Invoke(TcpClient client, String type, String method, params Object[] parameters)
        {
            Invoker i = new Invoker(type, method, parameters);

            //
            // send request
            client.Client.Send(Helper.StringToBytes(Serializer.SerializeObject(i)));

            //
            // receive answer
            Byte[] buffer = new Byte[client.ReceiveBufferSize];
            client.Client.Receive(buffer);

            String str = Helper.BytesToString(buffer);
            if (str.StartsWith("BAD_DATA"))
                throw new Exception("Got bad data from server!\r\n" + str.Substring("BAD_DATA".Length));

            return (Invoker)Serializer.DeSerializeObject(str);
        }

        public void RegisterAssembly(Assembly a)
        {
            foreach (Type t in a.GetTypes())
            {
                if (t.GetCustomAttributes(typeof(NetClass), false).Length > 0)
                {
                    RegisterType(t);
                }
            }
        }

        public void RegisterType(Type t)
        {
            NHType type = new NHType(t, this);
            if (!m_Types.Contains(type))
            {
                m_Types.Add(type);
            }
        }

        private NHType GetType(String type)
        {
            foreach (NHType t in m_Types)
            {
                if (t.Type.Name.ToLower() == type.ToLower())
                {
                    return t;
                }
            }

            return null;
        }

        public T GetInstance<T>()
        {
            foreach (NHType t in m_Types)
            {
                if (t.Type == typeof(T))
                    return (T)t.Instance;
            }

            return default(T);
        }

        public void InvokeOnAll(String method, params Object[] parameters)
        {
            foreach (NHType th in m_Types)
            {
                Type t = th.Type;
                MethodInfo mi = t.GetMethod(method);

                if (mi != null)
                {
                    mi.Invoke(th.Instance, parameters);
                }
            }
        }
    }
}
